package com.shouru.server;

import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.URL;

import com.shouru.server.ChangeScanner.AppChangeHandler;

public abstract class AbstarctServer implements Server,AppChangeHandler {
	private static final Long DEFAULT_SCAN_INTERVAL_SECONDS = 5L;
	private static final int DEFAULT_PORT = 80;
	private static final String DEFAULT_WEBAPP_DIR = "src/main/webapp";
	private static final String DEFAULT_CONTEXT = "/";
	private String webAppDir;
	private Integer port;
	private Long scanIntervalSeconds;
	private boolean running;
	private String context;
	private ExcudeScanResolver excudeScanResolver=new ExcudeScanResolver();
	/**
	 * 重启触发文件<br/>
	 * 该文件设置后，仅有该文件被修改时才会热加载
	 */
	private File restartTriggerFile;

	@Override
	public final void start() {
		
		if (isRunning()) {
			throw new IllegalStateException("服务器已经启动,不能重复开启");
		}
		
		if (getPort() < 0 || getPort() > 65536)
			throw new IllegalArgumentException("服务器端口没有指定或者不合法,合法端口：port >= 0 && port <= 65536");

		checkPort();
		doStart();
		//如果设置了修改检测间隔时间，则开始检测修改
		if(getScanIntervalSeconds()>0){
			new ChangeScanner()
				.setScanIntervalSeconds(getScanIntervalSeconds())	//检测间隔时间
				.setOnchange(this)	//修改事件监听者
				.setExcudeScanResolver(excudeScanResolver) //排除检测解析器
				.setTriggerFile(restartTriggerFile)	//热启动触发文件
				.start();
		}
		System.out.println("服务器启动成功:");
		System.out.println("\t 端口：" + getPort());
		System.out.println("\t Web根目录：" + getWebAppDir());
		System.out.println("\t 修改扫描间隔：" + getScanIntervalSeconds());
		setRunning(true);
		serverJoin();
	}

	protected abstract void serverJoin();

	@Override
	public void stop() {
		if (!isRunning()) {
			throw new IllegalStateException("服务器没有启动");
		}
		doStop();
		setRunning(false);
	}

	protected abstract void doStart();

	protected abstract void doStop();

	/**
	 * 检测端口是否被占用
	 * 
	 * @create_date 2016年7月14日
	 */
	private void checkPort() {
		ServerSocket ss = null;
		DatagramSocket ds = null;
		try {
			ss = new ServerSocket(getPort());
			ss.setReuseAddress(true);
			ds = new DatagramSocket(getPort());
			ds.setReuseAddress(true);
		} catch (Exception e) {
			throw new IllegalStateException("服务器启动失败：端口" + getPort() + "已占用");
		} finally {
			if (ds != null) {
				ds.close();
			}

			if (ss != null) {
				try {
					ss.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	@Override
	public Server port(int port) {
		this.port = port;
		return this;
	}

	public Server scanIntervalSeconds(Long scanIntervalSeconds) {
		this.scanIntervalSeconds = scanIntervalSeconds;
		return this;
	}

	public String getWebAppDir() {
		if (webAppDir == null) {
			webAppDir = DEFAULT_WEBAPP_DIR;
		}
		return webAppDir;
	}

	public Server webAppDir(String webAppDir) {
		this.webAppDir = webAppDir;
		return this;
	}

	public int getPort() {
		if (port == null) {
			port = DEFAULT_PORT;
		}
		return port;
	}

	public long getScanIntervalSeconds() {
		if (scanIntervalSeconds == null) {
			scanIntervalSeconds = DEFAULT_SCAN_INTERVAL_SECONDS;
		}
		return scanIntervalSeconds;
	}

	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	public String getContext() {
		if(context==null){
			context=DEFAULT_CONTEXT;
		}
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}
	
	@Override
	public Server addExcludeScanPath(String pattern) {
		excudeScanResolver.addExcludePath(pattern);
		return this;
	}


	public ExcudeScanResolver getExcudeScanResolver() {
		return excudeScanResolver;
	}

	public Server setExcudeScanResolver(ExcudeScanResolver excudeScanResolver) {
		this.excudeScanResolver = excudeScanResolver;
		return this;
	}

	@Override
	public Server triggerFile(String path) {
		if(!path.startsWith("/")){
			throw new IllegalArgumentException("triggerFile路径必须以‘/’开头，表明以项目跟路径开始");
		}
		File file=new File(getRootClassFile().getAbsolutePath()+path);
		if(!file.exists()||!file.isFile()){
			throw new IllegalArgumentException("triggerFile设置失败:没找到该文件");
		}
		this.restartTriggerFile=file;
		return this;
	}
	static File getRootClassFile() {
		URL url = ChangeScanner.class.getClassLoader().getResource("");
		try {
			return new File(url.toURI().getPath());
		} catch (Exception e) {
			return new File(url.getPath());
		}
	}
}
